﻿using Avalonia;
using Avalonia.Controls;
using Avalonia.Threading;
using Project2004.Jigsaw.Controls;
using Project2004.Jigsaw.Models;
using Project2004.Jigsaw.Utils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Project2004.Jigsaw.Presenters
{
    public class BlockPresenter
    {
        #region props
        public BlockId Id { get; private set; }
        #endregion

        #region fields

        private JigsawBlock2 _view;
        private MovableBlock _block;

        #endregion

        #region props

        public Point Position
        {
            get => _position;
            set
            {
                if (_position == value) return;

                Canvas.SetLeft(_view, value.X);
                Canvas.SetTop(_view, value.Y);
                _block.CanvasPoint = value;
                _position = value;
            }
        }
        private Point _position;

        public int ZIndex
        {
            get => _zindex; set
            {
                if (_zindex == value) return;
                _view.ZIndex = value;
                _zindex = value;
            }
        }
        private int _zindex;

        private BlockPresenterGroup _parent;
        private MainPresenter _root;

        #endregion

        #region ctors
        public BlockPresenter(BlockId id, JigsawBlock2 view, MovableBlock block, BlockPresenterGroup parent)
        {
            Id = id;
            _view = view;
            _block = block;
            _parent = parent;

            _view.Presenter = this;

            Position = new Point(0, 0);
            ZIndex = 0;

            view.PointerPressed += View_PointerPressed;
            view.PointerMoved += View_PointerMoved;
            view.PointerReleased += View_PointerReleased;

            _root = parent.Parent;
        }

        #endregion

        #region move

        private record struct BlockOldPositionInfo(BlockPresenter blockPrenseter, Point oldPoint);

        private static Point _dragStartPosition;

        private void View_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)
        {
            var parentCanvas = _view.Parent as Canvas;
            if (parentCanvas is null) return;

            var relativePoint = e.GetCurrentPoint(_view);

            _dragStartPosition = relativePoint.Position;

            _parent.Parent.SetFocus(this);

        }

        private void View_PointerMoved(object? sender, Avalonia.Input.PointerEventArgs e)
        {
            var parentCanvas = _view.Parent as Canvas;

            if (parentCanvas is null) return;

            var canvasPoint = e.GetCurrentPoint(parentCanvas);

            var isPressed = canvasPoint.Properties.IsLeftButtonPressed == true;

            if (isPressed)
            {
                var newPosition = canvasPoint.Position - _dragStartPosition;
                _parent.SetPosition(this, newPosition);
            }
        }

        private void View_PointerReleased(object? sender, Avalonia.Input.PointerReleasedEventArgs e)
        {
            var parentCanvas = _view.Parent as Canvas;
            if (parentCanvas is null) return;
            var canvasPoint = e.GetCurrentPoint(parentCanvas);
            var newPosition = canvasPoint.Position - _dragStartPosition;
            _parent.SetPosition(this, newPosition);

            CheckClose();
        }

        private void CheckClose()
        {
            var oldGroup = this._parent;
            var currentBlocks = new List<BlockPresenter>(oldGroup.Blocks);
            foreach (var currentBlock in oldGroup.Blocks)
            {
                foreach (var blockPresenter in _root.BlockPresenters)
                {
                    if (blockPresenter == currentBlock) continue;
                    if (IsClose(_root.Scene, currentBlock, blockPresenter))
                    {
                        var targetGroup = blockPresenter._parent;

                        if (targetGroup == oldGroup) continue;
                        Connect(oldGroup, targetGroup, blockPresenter);

                        //SetClose(_root.Scene, this, blockPresenter);
                        return;
                    }
                }
            }

        }

        private static void Connect(BlockPresenterGroup oldGroup, BlockPresenterGroup targetGroup, BlockPresenter targetBlock)
        {
            targetGroup.Blocks.AddRange(oldGroup.Blocks);

            targetGroup.Blocks.ForEach(i => i._parent = targetGroup);

            oldGroup.Blocks.Clear();

            targetGroup.UpdatePosition(targetBlock);
        }

        #endregion

        #region statics

        public static bool IsClose(Scene scene, BlockPresenter b1, BlockPresenter b2)
        {
            if (b1 == b2) throw new Exception("请不要让格子自己和自己进行判断！");
            var isNeigbor = IsNeigbor(b1, b2);

            if (isNeigbor == false) return false;

            var borderSize = scene.Size;
            var outter = FullConfigJigsawShapeData.GetOutter(borderSize);
            var boundSize = FullConfigJigsawShapeData.GetBoundSizeFromBorderLen(borderSize);
            var stdLen = borderSize + outter;

            var b1ConnectPoint = default(Vector);
            var b2ConnectPoint = default(Vector);

            if (b1.Id.X < b2.Id.X) // 左右布局
            {
                b1ConnectPoint = b1.Position + new Vector(stdLen, boundSize / 2);
                b2ConnectPoint = b2.Position + new Vector(outter, boundSize / 2);
            }
            else if (b1.Id.X > b2.Id.X)
            {
                b1ConnectPoint = b1.Position + new Vector(outter, boundSize / 2);
                b2ConnectPoint = b2.Position + new Vector(stdLen, boundSize / 2);
            }
            else if (b1.Id.Y < b2.Id.Y)
            {
                b1ConnectPoint = b1.Position + new Vector(boundSize / 2, stdLen);
                b2ConnectPoint = b2.Position + new Vector(boundSize / 2, outter);
            }
            else if (b1.Id.Y > b2.Id.Y)
            {
                b1ConnectPoint = b1.Position + new Vector(boundSize / 2, outter);
                b2ConnectPoint = b2.Position + new Vector(boundSize / 2, stdLen);
            }
            else
            {
                throw new NotImplementedException("发现了非相邻的单位格子");
            }
            var minDistance = Math.Min(60, borderSize / 6);

            var deltaVector = b1ConnectPoint - b2ConnectPoint;

            return deltaVector.Length < minDistance;
        }

        public static bool IsNeigbor(BlockPresenter b1, BlockPresenter b2)
        {
            var id1 = b1.Id;
            var id2 = b2.Id;
            if (id1.X != id2.X && id1.Y != id2.Y) return false;
            if (id1.X == id2.X && id1.Y == id2.Y) throw new Exception("请不要让格子自己和自己进行判断！");
            return true;
        }

        public static void SetClose(Scene scene, BlockPresenter block, BlockPresenter destTarget)
        {
            if (block == destTarget) throw new Exception("请不要让格子自己和自己进行判断！");
            var isNeigbor = IsNeigbor(block, destTarget);

            var borderSize = scene.Size;

            if (isNeigbor == false) return;

            if (block.Id.X < destTarget.Id.X) // 左右布局
            {
                block.Position = new Point(destTarget.Position.X - borderSize, destTarget.Position.Y);
            }
            else if (block.Id.X > destTarget.Id.X)
            {
                block.Position = new Point(destTarget.Position.X + borderSize, destTarget.Position.Y);
            }
            else if (block.Id.Y < destTarget.Id.Y)
            {
                block.Position = new Point(destTarget.Position.X, destTarget.Position.Y - borderSize);
            }
            else if (block.Id.Y > destTarget.Id.Y)
            {
                block.Position = new Point(destTarget.Position.X, destTarget.Position.Y + borderSize);
            }
            else
            {
                throw new NotImplementedException("发现了非相邻的单位格子");
            }

        }

        #endregion

    }
}
